package com.heima.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.heima.feign.ArticleFeignClient;
import com.heima.model.article.pojos.Article;
import com.heima.model.behavior.dtos.LikesBehaviorDto;
import com.heima.model.behavior.dtos.ReadBehaviorDto;
import com.heima.model.behavior.dtos.UnLikesBehaviorDto;
import com.heima.model.behavior.pojos.LikesBehavior;
import com.heima.model.behavior.pojos.ReadBehavior;
import com.heima.model.behavior.pojos.UnLikesBehavior;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.constants.BehaviorConstants;
import com.heima.model.constants.HotArticleConstants;
import com.heima.model.mess.UpdateArticleMess;
import com.heima.service.BehaviorService;
import com.heima.utils.UserThreadLocal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;

/**
 * @author chenshan
 */
@Service
public class BehaviorServiceImpl implements BehaviorService {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private ArticleFeignClient articleFeignClient;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    /**
     * 用户行为 - 给文章点赞(取消点赞)
     *
     * @param behaviorDto 点赞文章信息
     * @return ResponseResult
     */
    @Override
    public ResponseResult likesBehavior(LikesBehaviorDto behaviorDto) {
        // 校验参数的合法性
        if (!ObjectUtil.isAllNotEmpty(behaviorDto.getArticleId(), behaviorDto.getOperation(), behaviorDto.getType())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 校验文章id对应的文章是否存在
        Article article = articleFeignClient.findArticleById(behaviorDto.getArticleId());
        if (ObjectUtil.isEmpty(article)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章已不存在~");
        }

        // 接收用户id 和文章id
        Integer userId = UserThreadLocal.get();
        Long articleId = behaviorDto.getArticleId();
        if (ObjectUtil.isEmpty(userId)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        // 准备信息
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setArticleId(behaviorDto.getArticleId());
        mess.setType(UpdateArticleMess.UpdateArticleType.LIKES);

        // 用户给文章点赞啦
        if (behaviorDto.getOperation() == 0) {
            // 1.先判断Redis中是否有点赞记录
            Boolean userHasKey = redisTemplate.opsForHash().hasKey(BehaviorConstants.BEHAVIOR + userId,
                    BehaviorConstants.LIKES + articleId);
            Boolean articleHasKey = redisTemplate.opsForHash().hasKey(BehaviorConstants.BEHAVIOR + articleId,
                    BehaviorConstants.LIKES + userId);

            // 2.判断MongoDB中是否有点赞记录
            LikesBehavior likesBehavior = mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId).and("articleId").is(articleId)), LikesBehavior.class);

            // 3.Redis和MongoDB中存在点赞记录，不做任何操作， 返回已点赞
            if (ObjectUtil.isNotEmpty(likesBehavior) && userHasKey && articleHasKey) {
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            }

            // 4.Redis和MongoDB中不存在点在记录或点赞记录中user与article不对应，       添加 == 值任意
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + userId, BehaviorConstants.LIKES + articleId, 1);
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + articleId, BehaviorConstants.LIKES + userId, 1);
            if (ObjectUtil.isNull(likesBehavior)) {
                mongoTemplate.insert(LikesBehavior.builder().userId(userId).articleId(articleId).operation(0).type(behaviorDto.getType()).build());
            }

            // 修改文章点赞数量
            if (ObjectUtil.isEmpty(article.getLikes())) {
                article.setLikes(0);
            }
            // 点赞自增
            article.setLikes(article.getLikes() + 1);

            mess.setAdd(1);

        } else {
            // 用户取消点赞啦
            redisTemplate.opsForHash().delete(BehaviorConstants.BEHAVIOR + userId,
                    BehaviorConstants.LIKES + articleId);
            redisTemplate.opsForHash().delete(BehaviorConstants.BEHAVIOR + articleId,
                    BehaviorConstants.LIKES + userId);
            mongoTemplate.remove(Query.query(Criteria.where("userId").is(userId).and("articleId").is(articleId)), LikesBehavior.class);

            // 修改文章点赞数量
            if (ObjectUtil.isNotEmpty(article.getLikes()) && article.getLikes() > 0) {
                // 点赞自减
                article.setLikes(article.getLikes() - 1);
            }

            mess.setAdd(-1);
        }

        // 更新点赞数量
        articleFeignClient.updateArticle(article);

        //发送消息给kafka，数据聚合
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC,JSON.toJSONString(mess));

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 用户行为 - 阅读文章(阅读文章的次数)
     *
     * @param behaviorDto 用户阅读文章次数
     * @return ResponseResult
     */
    @Override
    public ResponseResult readBehavior(ReadBehaviorDto behaviorDto) {
        // 校验参数的合法性
        if (ObjectUtil.isEmpty(behaviorDto.getArticleId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        if (ObjectUtil.isEmpty(behaviorDto.getCount())) {
            behaviorDto.setCount(1);
        }

        // 校验文章id对应的文章是否存在
        Article article = articleFeignClient.findArticleById(behaviorDto.getArticleId());
        if (ObjectUtil.isEmpty(article)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章已不存在~");
        }

        // 接收用户id 和文章id
        Integer userId = UserThreadLocal.get();
        Long articleId = behaviorDto.getArticleId();
        if (ObjectUtil.isEmpty(userId)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        // 用户阅读文章啦
        // 1.先判断Redis中是否有当前用户的阅读记录
        // Boolean userHasKey = redisTemplate.opsForHash().hasKey(BehaviorConstants.BEHAVIOR + userId, BehaviorConstants.READ + articleId);
        // Boolean ArticleHasKey = redisTemplate.opsForHash().hasKey(BehaviorConstants.BEHAVIOR + articleId, BehaviorConstants.READ + userId);

        // 2.判断MongoDB中是否有当前用户的阅读记录(以MongoDB为准)
        ReadBehavior readBehavior = mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId).and("articleId").is(articleId)), ReadBehavior.class);

        // 3.Redis和MongoDB中存在当前用户的阅读记录，用户阅读次数加一
        if (ObjectUtil.isNotEmpty(readBehavior)) {
            mongoTemplate.updateFirst(Query.query(Criteria.where("userId").is(userId).and("articleId").is(articleId)),
                    Update.update("count", readBehavior.getCount() + 1), ReadBehavior.class);
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + userId,
                    BehaviorConstants.READ + articleId, readBehavior.getCount() + 1);
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + articleId,
                    BehaviorConstants.READ + userId, readBehavior.getCount() + 1);
        } else {
            // 4.Redis和MongoDB中不存在当前用户的阅读记录 新增一条记录
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + userId,
                    BehaviorConstants.READ + articleId, 1);
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + articleId,
                    BehaviorConstants.READ + userId, 1);
            mongoTemplate.insert(ReadBehavior.builder().userId(userId).articleId(articleId).count(1).build());
        }

        // 文章阅读数量加一
        if (ObjectUtil.isEmpty(article.getViews())) {
            article.setViews(0);
        }
        article.setViews(article.getViews() + 1);
        articleFeignClient.updateArticle(article);

        //发送消息给kafka，数据聚合
        //发送消息，数据聚合
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setArticleId(behaviorDto.getArticleId());
        mess.setType(UpdateArticleMess.UpdateArticleType.VIEWS);
        mess.setAdd(1);
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC,JSON.toJSONString(mess));

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 用户行为 - 不喜欢此文章
     *
     * @param behaviorDto 是否不喜欢
     * @return ResponseResult
     */
    @Override
    public ResponseResult unlikesBehavior(UnLikesBehaviorDto behaviorDto) {
        // 校验参数的合法性
        if (!ObjectUtil.isAllNotEmpty(behaviorDto.getArticleId(), behaviorDto.getType())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 校验文章id对应的文章是否存在
        ResponseResult result = articleFeignClient.findOneById(behaviorDto.getArticleId());
        if (ObjectUtil.isEmpty(result.getData())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章已不存在~");
        }

        // 接收用户id 和文章id
        Integer userId = UserThreadLocal.get();
        Long articleId = behaviorDto.getArticleId();
        if (ObjectUtil.isEmpty(userId)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        // 用户不喜欢此文章
        if (behaviorDto.getType() == 0) {
            // 1.先判断Redis中是否有不喜欢记录
            Boolean userHasKey = redisTemplate.opsForHash().hasKey(BehaviorConstants.BEHAVIOR + userId,
                    BehaviorConstants.UN_LIKES + articleId);
            Boolean ArticleHasKey = redisTemplate.opsForHash().hasKey(BehaviorConstants.BEHAVIOR + articleId,
                    BehaviorConstants.UN_LIKES + userId);

            // 2.判断MongoDB中是否有不喜欢记录
            UnLikesBehavior unLikesBehavior = mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId).and("articleId").is(articleId)), UnLikesBehavior.class);

            // 3.Redis和MongoDB中存在点赞记录，不做任何操作， 返回已点赞
            if (ObjectUtil.isNotEmpty(unLikesBehavior) && userHasKey && ArticleHasKey) {
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            }

            // 4.Redis和MongoDB中不存在不喜欢记录或不喜欢记录中user与article不对应       添加 == 值任意
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + userId, BehaviorConstants.UN_LIKES + articleId, 1);
            redisTemplate.opsForHash().put(BehaviorConstants.BEHAVIOR + articleId, BehaviorConstants.UN_LIKES + userId, 1);
            if (ObjectUtil.isNull(unLikesBehavior)) {
                mongoTemplate.insert(UnLikesBehavior.builder().userId(userId).articleId(articleId).type(behaviorDto.getType()).build());
            }

        } else {
            // 用户取消不喜欢啦
            redisTemplate.opsForHash().delete(BehaviorConstants.BEHAVIOR + userId,
                    BehaviorConstants.UN_LIKES + articleId);
            redisTemplate.opsForHash().delete(BehaviorConstants.BEHAVIOR + articleId,
                    BehaviorConstants.UN_LIKES + userId);
            mongoTemplate.remove(Query.query(Criteria.where("userId").is(userId).and("articleId").is(articleId)), UnLikesBehavior.class);

        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}
